/*
 * Copyright (C) 2021 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <string.h>
#include <stdlib.h>
#include "lwip/netif.h"
#include "lwip/netifapi.h"
#include "os.h"
#include "xr_wifi_adapter.h"
#include "net/wlan/wlan_ext_req.h"

static xr_wifi_event_cb g_evt_cb = NULL;

static xr_wifi_ap_sta_info g_softap_sta_list[XR_WIFI_MAX_AP_STA_NUM];
static int g_softap_sta_index[XR_WIFI_MAX_AP_STA_NUM];

static void xr_wifi_scan_done_callback()
{
	xr_wifi_event e;
	int res_num = 0;

	if (g_evt_cb == NULL)
		return;

	if (0 != wlan_sta_get_scan_result_num(&res_num))
		return;

	e.info.wifi_scan_done.bss_num = res_num;
	e.event = XR_WIFI_EVT_SCAN_DONE;

	g_evt_cb(&e);
}

static xr_wifi_status g_sta_connected_info;

static void xr_wifi_connected_callback()
{
	xr_wifi_event e;

	if (g_evt_cb == NULL)
		return;

	if (XR_WIFI_OK != xr_wifi_sta_get_connect_info(&g_sta_connected_info))
		return;

	e.info.wifi_connected.ssid_len = strlen(g_sta_connected_info.ssid) + 1;
	memcpy(e.info.wifi_connected.ssid, g_sta_connected_info.ssid,
	       strlen(g_sta_connected_info.ssid));
	WIFI_MAC_ADDR_COPY(e.info.wifi_connected.bssid,
			   g_sta_connected_info.bssid);
	e.event = XR_WIFI_EVT_CONNECTED;

	g_evt_cb(&e);
}

static void xr_wifi_disconnected_callback()
{
	xr_wifi_event e;

	if (g_evt_cb == NULL)
		return;

	WIFI_MAC_ADDR_COPY(e.info.wifi_disconnected.bssid,
			   g_sta_connected_info.bssid);
	e.info.wifi_disconnected.reason_code = -1; /* faked */
	e.event = XR_WIFI_EVT_DISCONNECTED;

	g_sta_connected_info.status = XR_WIFI_DISCONNECTED;

	g_evt_cb(&e);
}

static void xr_wifi_sta_no_network_callback()
{
	xr_wifi_event e;

	if (g_evt_cb == NULL)
		return;

	e.event = XR_WIFI_EVT_STA_FCON_NO_NETWORK;

	g_evt_cb(&e);
}

static int softap_set_sta_join(xr_wifi_ap_sta_info *new_list,
			       uint32_t new_list_num)
{
	int i = 0, j = 0;
	int idle_idx = -1, finded = 0;

	j = 0;
	while (j++ < XR_WIFI_MAX_AP_STA_NUM) {
		if (g_softap_sta_index[j] == 0) {
			idle_idx = j;
			break;
		}
	}

	for (i = 0; i < new_list_num; i++) {
		finded = 0;
		j = 0;
		while (j < XR_WIFI_MAX_AP_STA_NUM) {
			if (g_softap_sta_index[j] == 0) {
				++j;
				continue;
			}

			if (WIFI_MAC_ADDR_EQ(new_list[i].mac,
					     g_softap_sta_list[j].mac)) {
				finded = 1;
				break;
			}
			++j;
		}

		if (finded == 0) {
			g_softap_sta_index[idle_idx] = 1;
			WIFI_MAC_ADDR_COPY(g_softap_sta_list[idle_idx].mac,
					   new_list[i].mac);
			break;
		}
	}

	if (i < new_list_num)
		return idle_idx;
	else
		return -1;
}

static int softap_set_sta_leave(xr_wifi_ap_sta_info *new_list,
				uint32_t new_list_num)
{
	int i = 0, j = 0, finded;

	while (j < XR_WIFI_MAX_AP_STA_NUM) {
		finded = 0;
		if (g_softap_sta_index[j] == 0) {
			++j;
			continue;
		}

		for (i = 0; i < new_list_num; i++) {
			if (WIFI_MAC_ADDR_EQ(new_list[i].mac,
					     g_softap_sta_list[j].mac)) {
				finded = 1;
				break;
			}
		}

		if (finded == 0) {
			g_softap_sta_index[j] = 0;
			break;
		}
		++j;
	}

	if (j < XR_WIFI_MAX_AP_STA_NUM)
		return j;
	else
		return -1;
}

static void xr_wifi_ap_sta_connected_callback()
{
	xr_wifi_event e;
	uint32_t sta_list_num;
	uint32_t join_idx;

	if (g_evt_cb == NULL)
		return;

	sta_list_num = XR_WIFI_MAX_AP_STA_NUM;
	xr_wifi_ap_sta_info *sta_list =
		malloc(sizeof(xr_wifi_ap_sta_info) * sta_list_num);

	if (XR_WIFI_OK !=
	    xr_wifi_softap_get_connected_sta(sta_list, &sta_list_num)) {
		return;
	}
	join_idx = softap_set_sta_join(sta_list, sta_list_num);

	WIFI_MAC_ADDR_COPY(e.info.ap_sta_connected.addr,
			   g_softap_sta_list[join_idx].mac);
	e.event = XR_WIFI_EVT_STA_CONNECTED;

	g_evt_cb(&e);
	free(sta_list);
}

static void xr_wifi_ap_sta_disconnected_callback()
{
	xr_wifi_event e;
	uint32_t sta_list_num;
	uint32_t leave_idx;

	if (g_evt_cb == NULL)
		return;

	sta_list_num = XR_WIFI_MAX_AP_STA_NUM;
	xr_wifi_ap_sta_info *sta_list =
		malloc(sizeof(xr_wifi_ap_sta_info) * sta_list_num);

	if (XR_WIFI_OK !=
	    xr_wifi_softap_get_connected_sta(sta_list, &sta_list_num)) {
		return;
	}
	leave_idx = softap_set_sta_leave(sta_list, sta_list_num);

	WIFI_MAC_ADDR_COPY(e.info.ap_sta_disconnected.addr,
			   g_softap_sta_list[leave_idx].mac);
	e.info.ap_sta_disconnected.reason_code = -1; /* faked */
	e.event = XR_WIFI_EVT_STA_DISCONNECTED;

	g_evt_cb(&e);
	free(sta_list);
}

static void xr_wifi_ap_started_callback()
{
	if (g_evt_cb == NULL)
		return;

	xr_wifi_event e;
	e.event = XR_WIFI_EVT_AP_START;

	g_evt_cb(&e);
}

static void xr_wifi_event_process(uint32_t event, uint32_t data, void *arg)
{
	struct sysinfo *sysinfo = sysinfo_get();

	uint16_t type = EVENT_SUBTYPE(event);

	switch (type) {
	case NET_CTRL_MSG_WLAN_SCAN_SUCCESS:
		xr_wifi_scan_done_callback();
		break;
	case NET_CTRL_MSG_WLAN_SCAN_FAILED:
		break;
	case NET_CTRL_MSG_WLAN_CONNECTED:
		if (sysinfo->wlan_mode == WLAN_MODE_STA)
			xr_wifi_connected_callback();
		break;
	case NET_CTRL_MSG_WLAN_DISCONNECTED:
		if (sysinfo->wlan_mode == WLAN_MODE_STA)
			xr_wifi_disconnected_callback();
		break;
	case NET_CTRL_MSG_NETWORK_UP:
		if (sysinfo->wlan_mode == WLAN_MODE_HOSTAP)
			xr_wifi_ap_started_callback();
		break;
	case NET_CTRL_MSG_WLAN_AP_STA_CONNECTED:
		if (sysinfo->wlan_mode == WLAN_MODE_HOSTAP)
			xr_wifi_ap_sta_connected_callback();
		break;
	case NET_CTRL_MSG_WLAN_AP_STA_DISCONNECTED:
		if (sysinfo->wlan_mode == WLAN_MODE_HOSTAP)
			xr_wifi_ap_sta_disconnected_callback();
		break;
	case NET_CTRL_MSG_CONNECTION_LOSS:
	case NET_CTRL_MSG_WLAN_CONNECT_FAILED:
		if (sysinfo->wlan_mode == WLAN_MODE_STA)
			xr_wifi_sta_no_network_callback();
		break;
	case NET_CTRL_MSG_NETWORK_DOWN:
	case NET_CTRL_MSG_WLAN_4WAY_HANDSHAKE_FAILED:
	default:
		break;
	}
}

static observer_base *net_ob = NULL;

int xr_wifi_event_oberser_create()
{
	if (net_ob != NULL) {
		return XR_WIFI_OK;
	}

	net_ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK,
					      NET_CTRL_MSG_ALL,
					      xr_wifi_event_process, NULL);

	if (net_ob == NULL || sys_ctrl_attach(net_ob) != 0) {
		return XR_WIFI_FAIL;
	}

	return XR_WIFI_OK;
}

int xr_wifi_event_oberser_destroy()
{
	if (0 == sys_callback_observer_destroy(net_ob))
		return XR_WIFI_OK;
	else
		return XR_WIFI_FAIL;
}

int xr_wifi_register_event_callback(xr_wifi_event_cb event_cb)
{
	g_evt_cb = event_cb;
	return XR_WIFI_OK;
}

int xr_wifi_config_callback(uint8_t mode, uint8_t task_prio, uint8_t stack_size)
{
	if (mode == 1) {
		return xr_wifi_event_oberser_create();
	} else {
		return xr_wifi_event_oberser_destroy();
	}
}

static void set_ap_param(int auth_alg, int key_mgmt, int wpa_cipher,
			 int rsn_cipher, int proto)
{
	wlan_ap_config_t config;

	config.field = WLAN_STA_FIELD_AUTH_ALG;
	config.u.auth_alg = auth_alg;
	wlan_ap_set_config(&config);

	config.field = WLAN_AP_FIELD_KEY_MGMT;
	config.u.key_mgmt = key_mgmt;
	wlan_ap_set_config(&config);

	config.field = WLAN_AP_FIELD_WPA_PAIRWISE_CIPHER;
	config.u.wpa_cipher = wpa_cipher;
	wlan_ap_set_config(&config);

	config.field = WLAN_AP_FIELD_RSN_PAIRWISE_CIPHER;
	config.u.rsn_cipher = rsn_cipher;
	wlan_ap_set_config(&config);

	config.field = WLAN_AP_FIELD_PROTO;
	config.u.proto = proto;
	wlan_ap_set_config(&config);
}

int xr_wifi_softap_start(xr_wifi_softap_config *conf, char *ifname, int *len)
{
	wlan_ap_config_t config;

	if (0 != net_switch_mode(WLAN_MODE_HOSTAP)) {
		return XR_WIFI_FAIL;
	}

	wlan_ap_set((uint8_t *)conf->ssid, strlen(conf->ssid),
		    (uint8_t *)conf->key);

	config.field = WLAN_AP_FIELD_CHANNEL;
	config.u.channel = conf->channel_num;
	wlan_ap_set_config(&config);

	config.field = WLAN_AP_FIELD_IEEE80211N;
	config.u.ieee80211n = 1;
	wlan_ap_set_config(&config);

	switch (conf->authmode) {
	case XR_WIFI_SECURITY_OPEN:
		set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_NONE, 0, 0, 0);
		break;

	case XR_WIFI_SECURITY_WPAPSK_WPA2PSK_MIX:
		set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_PSK,
			     WPA_CIPHER_TKIP, WPA_CIPHER_CCMP,
			     WPA_PROTO_WPA | WPA_PROTO_RSN);
		break;

	case XR_WIFI_SECURITY_WPA2PSK:
		set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_PSK, 0,
			     WPA_CIPHER_CCMP, WPA_PROTO_RSN);
		break;

	case XR_WIFI_SECURITY_WPAPSK:
		set_ap_param(WPA_AUTH_ALG_OPEN, WPA_KEY_MGMT_PSK,
			     WPA_CIPHER_TKIP, 0, WPA_PROTO_WPA);
		break;

	case XR_WIFI_SECURITY_WEP:
		set_ap_param(WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED,
			     WPA_KEY_MGMT_NONE,
			     WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104,
			     WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104,
			     WPA_PROTO_WPA | WPA_PROTO_RSN);
		break;

	/* TODO: XR_WIFI_SECURITY_SAE (WPA3) */
	case XR_WIFI_SECURITY_SAE:

	/* The following items are not needed for the time being */
	case XR_WIFI_SECURITY_WPA:
	case XR_WIFI_SECURITY_WPA2:
	case XR_WIFI_SECURITY_UNKNOWN:

	default:
		return XR_WIFI_FAIL;
	}

	wlan_ap_disable();

	if (0 != wlan_ap_enable())
		return XR_WIFI_FAIL;

	WIFI_SET_AP_IFNAME(ifname, len);

	return XR_WIFI_OK;
}

int xr_wifi_softap_stop(void)
{
	return (wlan_ap_disable() == 0) ? XR_WIFI_OK : XR_WIFI_FAIL;
}

int xr_wifi_softap_get_connected_sta(xr_wifi_ap_sta_info *sta_list,
				     uint32_t *sta_num)
{
	wlan_ap_stas_t stas;

	stas.sta = (wlan_ap_sta_t *)sta_list;
	stas.size = *sta_num;

	if (wlan_ap_sta_info(&stas) != 0) {
		return XR_WIFI_FAIL;
	}

	*sta_num = stas.num;

	return XR_WIFI_OK;
}

int xr_wifi_get_channel(const char *ifname, uint8_t ifname_len)
{
	if (ifname[0] != 'a' || ifname[1] != 'p') {
		return XR_WIFI_INVALID_CHANNEL;
	}

	wlan_ap_config_t config;
	config.field = WLAN_AP_FIELD_CHANNEL;
	wlan_ap_get_config(&config);

	return config.u.channel;
}

int xr_wifi_sta_start(char *ifname, int *len)
{
	WIFI_SET_STA_IFNAME(ifname, len);
	return (net_switch_mode(WLAN_MODE_STA) == 0) ? XR_WIFI_OK :
							     XR_WIFI_FAIL;
}

int xr_wifi_sta_set_reconnect_policy(int enable, uint32_t seconds,
				     uint32_t period, uint32_t max_try_count)
{
	wlan_sta_set_autoconnect(enable);
	return XR_WIFI_OK;
}

int xr_wifi_sta_stop(void)
{
	return (wlan_sta_disable() == 0) ? XR_WIFI_OK : XR_WIFI_FAIL;
}

static int xr_wifi_normal_connect(char *ssid, char *passwd)
{
	uint8_t *psk;
	uint8_t ssid_len;

	if (ssid)
		ssid_len = strlen(ssid);
	else
		goto err;

	if (ssid_len > WLAN_SSID_MAX_LEN)
		ssid_len = WLAN_SSID_MAX_LEN;

	if (0 != net_switch_mode(WLAN_MODE_STA))
		goto err;

	if (0 != wlan_sta_disable())
		goto err;

	if (passwd) {
		if (strlen(passwd) == 0)
			psk = NULL;
		else
			psk = (uint8_t *)passwd;
	} else {
		psk = NULL;
	}

	if (0 != wlan_sta_set((uint8_t *)ssid, ssid_len, psk))
		goto err;

	if (0 != wlan_sta_enable())
		goto err;

	return XR_WIFI_OK;

err:
	return XR_WIFI_FAIL;
}

static int wep_passwd_check(uint8_t passwd_len)
{
	if (passwd_len == XR_WIFI_WEP40_ASCII_LEN ||
	    passwd_len == XR_WIFI_WEP40_HEX_LEN ||
	    passwd_len == XR_WIFI_WEP104_ASCII_LEN ||
	    passwd_len == XR_WIFI_WEP104_HEX_LEN)
		return 1;
	else
		return 0;
}

static int xr_wifi_wep_connect(char *ssid, char *passwd)
{
	uint8_t ssid_len;
	wlan_sta_config_t config;

	if (ssid)
		ssid_len = strlen(ssid);
	else
		goto err;

	if (!wep_passwd_check(strlen(passwd)))
		goto err;

	if (ssid_len > WLAN_SSID_MAX_LEN)
		ssid_len = WLAN_SSID_MAX_LEN;

	if (0 != net_switch_mode(WLAN_MODE_STA))
		goto err;

	if (0 != wlan_sta_disable())
		goto err;

	memset(&config, 0, sizeof(config));

	config.field = WLAN_STA_FIELD_SSID;
	memcpy(config.u.ssid.ssid, ssid, ssid_len);
	config.u.ssid.ssid_len = ssid_len;
	if (0 != wlan_sta_set_config(&config))
		goto err;

	config.field = WLAN_STA_FIELD_WEP_KEY0;
	strlcpy((char *)config.u.wep_key, passwd, sizeof(config.u.wep_key));
	if (0 != wlan_sta_set_config(&config))
		goto err;

	config.field = WLAN_STA_FIELD_WEP_KEY_INDEX;
	config.u.wep_tx_keyidx = 0;
	if (0 != wlan_sta_set_config(&config))
		goto err;

	config.field = WLAN_STA_FIELD_AUTH_ALG;
	config.u.auth_alg = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
	if (0 != wlan_sta_set_config(&config))
		goto err;

	config.field = WLAN_STA_FIELD_KEY_MGMT;
	config.u.key_mgmt = WPA_KEY_MGMT_NONE;
	if (0 != wlan_sta_set_config(&config))
		goto err;

	if (0 != wlan_sta_enable())
		goto err;

	return XR_WIFI_OK;

err:
	return XR_WIFI_FAIL;
}

int xr_wifi_sta_connect(xr_wifi_assoc_request *req)
{
	int ret;

	if (req == NULL) {
		return XR_WIFI_FAIL;
	}

	switch (req->auth) {
	case XR_WIFI_SECURITY_OPEN:
		*(req->key) = NULL;
	case XR_WIFI_SECURITY_WPAPSK:
	case XR_WIFI_SECURITY_WPA2PSK:
	case XR_WIFI_SECURITY_WPAPSK_WPA2PSK_MIX:
	case XR_WIFI_SECURITY_WPA:
	case XR_WIFI_SECURITY_WPA2:
	case XR_WIFI_SECURITY_SAE:
		ret = xr_wifi_normal_connect(req->ssid, req->key);
		break;
	case XR_WIFI_SECURITY_WEP:
		ret = xr_wifi_wep_connect(req->ssid, req->key);
		break;
	default:
		ret = XR_WIFI_FAIL;
		break;
	}

	return ret;
}

int xr_wifi_sta_fast_connect(xr_wifi_fast_assoc_request *fast_request)
{
	return xr_wifi_sta_connect(&fast_request->req);
}

int xr_wifi_sta_get_connect_info(xr_wifi_status *connect_status)
{
	wlan_sta_ap_t ap_info;
	wlan_sta_states_t state;

	wlan_sta_state(&state);
	switch (state) {
	case WLAN_STA_STATE_DISCONNECTED:
		connect_status->status = XR_WIFI_DISCONNECTED;
		break;
	case WLAN_STA_STATE_CONNECTED:
		connect_status->status = XR_WIFI_CONNECTED;
		break;
	default:
		break;
	}

	if (connect_status->status != XR_WIFI_DISCONNECTED) {
		wlan_sta_ap_info(&ap_info);
		memcpy(connect_status->ssid, ap_info.ssid.ssid,
		       ap_info.ssid.ssid_len);
		WIFI_MAC_ADDR_COPY(connect_status->bssid, ap_info.bssid);
		connect_status->channel = ap_info.channel;
	}

	return XR_WIFI_OK;
}

int xr_wifi_sta_scan(void)
{
	int ret = -1;

	ret = wlan_sta_bss_max_count(64);
	if (ret < 0) {
		return (ret == 0 || ret == -2) ? XR_WIFI_OK : XR_WIFI_FAIL;
	}

	ret = wlan_sta_scan_once();
	return (ret == 0 || ret == -2) ? XR_WIFI_OK : XR_WIFI_FAIL;
}

int xr_wifi_sacn_freq(uint8_t channel)
{
	int ret = 0, i = 0;
	int freq[14];
	wlan_ext_scan_freq_t param = { 0 };

	if ((channel == 0) || (channel >= 14)) {
		return XR_WIFI_FAIL;
	}

	freq[0] = (2704 + 5 * channel);

	param.freq_num = 1;
	param.freq_num = freq;

	return wlan_ext_request(g_wlan_netif, WLAN_EXT_CMD_SET_SCAN_FREQ,
				(int)(&param)) == 0 ?
			     XR_WIFI_OK :
			     XR_WIFI_FAIL;
}

int xr_wifi_sta_advance_scan(xr_wifi_scan_params *sp)
{
	int ret = -1, TimeOutCNT = 0;
	wlan_sta_config_t sta_config;
	wlan_sta_scan_param_t param;
	unsigned char errorbssid[6] = { 0, 0, 0, 0, 0, 0 };
	switch (sp->scan_type) {
	case XR_WIFI_BASIC_SCAN:
		ret = wlan_sta_scan_once();
		while (ret == -2) { //time out
			OS_MSleep(100);
			ret = wlan_sta_scan_once();
			if ((++TimeOutCNT) >= 20) {
				break;
			}
		}
		break;

	case XR_WIFI_SSID_SCAN:
		if (sp->ssid_len != 0) {
			sta_config.field = WLAN_STA_FIELD_SSID;
			sta_config.u.ssid.ssid_len =
				strlen((const char *)sp->ssid);
			memcpy(sta_config.u.ssid.ssid, sp->ssid,
			       WLAN_SSID_MAX_LEN);
			wlan_sta_set_config(&sta_config);

			sta_config.field = WLAN_STA_FIELD_SCAN_SSID;
			sta_config.u.scan_ssid = 1;
			wlan_sta_set_config(&sta_config);

			param.scan_only = 0;
			param.scan_passive = 0;
			param.scan_ssid = 1;

			ret = wlan_sta_scan(&param);
		}
		break;

	case XR_WIFI_CHANNEL_SCAN:
		if (xr_wifi_sacn_freq(sp->channel) == XR_WIFI_OK) {
			ret = wlan_sta_scan_once();
		}
		break;
	//case XR_WIFI_SSID_PREFIX_SCAN:
	case XR_WIFI_BSSID_SCAN:
		if (memcmp(sp->bssid, errorbssid, 6) != 0) {
			ret = wlan_sta_scan_once();
			while (ret == -2) { //time out
				OS_MSleep(100);
				ret = wlan_sta_scan_once();
				if ((++TimeOutCNT) >= 20) {
					break;
				}
			}
		}
		break;
	default:
		ret = -1;
		break;
	}

	return ret == 0 ? XR_WIFI_OK : XR_WIFI_FAIL;
}

int xr_wifi_sta_disconnect(void)
{
	if (0 != wlan_sta_disable()) {
		return XR_WIFI_FAIL;
	}

	return XR_WIFI_OK;
}

static xr_wifi_auth_mode parse_auth_mode(wlan_sta_ap_t *ap)
{
	if (ap->wpa_flags & WPA_FLAGS_ESS) {
		if (ap->wpa_flags & WPA_FLAGS_WEP)
			return XR_WIFI_SECURITY_WEP;

		if (ap->wpa_flags & WPA_FLAGS_WPA &&
		    ap->wpa_flags & WPA_FLAGS_WPA2)
			if (ap->wpa_key_mgmt & WPA_KEY_MGMT_PSK &&
			    ap->wpa2_key_mgmt & WPA_KEY_MGMT_PSK)
				return XR_WIFI_SECURITY_WPAPSK_WPA2PSK_MIX;

		if (ap->wpa_flags & WPA_FLAGS_WPA) {
			if (ap->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
				return XR_WIFI_SECURITY_WPAPSK;
			else
				return XR_WIFI_SECURITY_WPA;
		}

		if (ap->wpa_flags & WPA_FLAGS_WPA2) {
			if (ap->wpa2_key_mgmt & WPA_KEY_MGMT_PSK)
				return XR_WIFI_SECURITY_WPA2PSK;
			else
				return XR_WIFI_SECURITY_WPA2;
		}

		return XR_WIFI_SECURITY_OPEN;
	}

	/*
    if (ap->wpa_key_mgmt == WPA_KEY_MGMT_NONE &&
        ap->wpa2_key_mgmt == WPA_KEY_MGMT_NONE)
        return XR_WIFI_SECURITY_OPEN;
    */

	/* TODO: XR_WIFI_SECURITY_SAE (WPA3) */

	return XR_WIFI_SECURITY_UNKNOWN;
}

int xr_wifi_sta_scan_results(xr_wifi_ap_info *ap_list, uint8_t *ap_num)
{
	int i;
	int ret = XR_WIFI_FAIL;
	wlan_sta_scan_results_t results;

	results.size = *ap_num;
	results.ap = malloc(results.size * sizeof(wlan_sta_ap_t));
	if (results.ap == NULL) {
		return XR_WIFI_FAIL;
	}

	memset(ap_list, 0, sizeof(xr_wifi_ap_info) * (*ap_num));

	if (wlan_sta_scan_result(&results) == 0) {
		for (i = 0; i < results.num; i++) {
			memcpy(ap_list[i].ssid, results.ap[i].ssid.ssid,
			       results.ap[i].ssid.ssid_len);
			WIFI_MAC_ADDR_COPY(ap_list[i].bssid,
					   results.ap[i].bssid);
			ap_list[i].channel = results.ap[i].channel;
			ap_list[i].rssi = results.ap[i].rssi;
			ap_list[i].auth = parse_auth_mode(&results.ap[i]);
		}
		ret = XR_WIFI_OK;
	} else {
		ret = XR_WIFI_FAIL;
	}

	*ap_num = results.num;
	free(results.ap);
	return ret;
}

int xr_wifi_sta_get_ap_rssi(void)
{
	wlan_sta_ap_t ap_info;
	wlan_sta_ap_info(&ap_info);
	return ap_info.rssi;
}

int xr_wifi_get_macaddr(char *mac_addr, uint8_t mac_len)
{
	struct sysinfo *sysinfo;

	if (mac_len != WIFI_MAC_ADDR_LEN)
		return XR_WIFI_FAIL;

	sysinfo = sysinfo_get();
	WIFI_MAC_ADDR_COPY(mac_addr, sysinfo->mac_addr);

	return XR_WIFI_OK;
}

int xr_wifi_get_local_ip(const char *netif_name, int *const ip)
{
	int ret;
	struct netif *netif_node = netif_find(netif_name);
	if (netif_node == NULL) {
		XR_WIFI_ERR("netif find fail\r\n");
		return XR_WIFI_FAIL;
	}

#ifdef __CONFIG_LWIP_V1
	ip_addr_t ipAddr;
	ip_addr_t netMask;
	ip_addr_t gateWay;
#elif LWIP_IPV4
	ip4_addr_t ipAddr;
	ip4_addr_t netMask;
	ip4_addr_t gateWay;
#endif

	ret = netifapi_netif_get_addr(netif_node, &ipAddr, &netMask, &gateWay);
	if (ret == 0) {
		*ip = ip4_addr_get_u32(&ipAddr);
		return XR_WIFI_OK;
	}
	return XR_WIFI_FAIL;
}

void xr_wifi_set_local_ip(const char *netif_name, int gw, int ipaddr,
			  int netmask)
{
#ifdef __CONFIG_LWIP_V1
	ip_addr_t st_gw;
	ip_addr_t st_ipaddr;
	ip_addr_t st_netmask;
#elif LWIP_IPV4
	ip4_addr_t st_gw;
	ip4_addr_t st_ipaddr;
	ip4_addr_t st_netmask;
#endif

	struct netif *netif_node = netif_find(netif_name);
	if (netif_node == NULL) {
		XR_WIFI_ERR("netif find fail\r\n");
		return XR_WIFI_FAIL;
	}

	ip4_addr_set_u32(&st_gw, gw);
	ip4_addr_set_u32(&st_ipaddr, ipaddr);
	ip4_addr_set_u32(&st_netmask, netmask);
	netifapi_netif_set_addr(netif_node, &st_ipaddr, &st_netmask, &st_gw);
	return;
}

void xr_wifi_set_dns_server(int pos, int dns_server)
{
#ifdef __CONFIG_LWIP_V1
	ip_addr_t dns_srv_addr;
#elif LWIP_IPV4
	ip4_addr_t dns_srv_addr;
#endif

	ip4_addr_set_u32(&dns_srv_addr, dns_server);
	/* TODO: lwip_dns_setserver(pos, &dns_srv_addr); */
}

void xr_wifi_dhcp_enable(const char *netif_name, int enable)
{
	struct netif *netif_node = netif_find(netif_name);
	if (netif_node == NULL) {
		XR_WIFI_ERR("netif find fail\r\n");
		return XR_WIFI_FAIL;
	}

	if (enable)
		netifapi_dhcp_start(netif_node);
	else
		netifapi_dhcp_stop(netif_node);
}

void xr_wifi_netif_status_set(const char *netif_name, int up)
{
	struct netif *netif_node = netif_find(netif_name);
	if (netif_node == NULL) {
		XR_WIFI_ERR("netif find fail\r\n");
		return XR_WIFI_FAIL;
	}

	if (up) {
		netifapi_netif_set_link_up(netif_node);
		netifapi_netif_set_up(netif_node);
	} else {
		netifapi_netif_set_link_down(netif_node);
		netifapi_netif_set_down(netif_node);
	}
}

int xr_wifi_set_appie(uint8_t type, uint8_t *ie, uint16_t ie_len)
{
	int ret;
	struct netif *nif = g_wlan_netif;

	ret = wlan_set_appie(nif, type, ie, ie_len);
	if (ret != 0)
		return XR_WIFI_FAIL;

	return XR_WIFI_OK;
}
